<template>
  <view class="uni-forms-item" :class="['is-direction-' + localLabelPos, border ? 'uni-forms-item--border' : '', border && isFirstBorder ? 'is-first-border' : '']">
    <slot name="label">
      <view class="uni-forms-item__label" :class="{ 'no-label': !label && !required }" :style="{ width: localLabelWidth, justifyContent: localLabelAlign }">
        <text v-if="required" class="is-required">*</text>
        <text>{{ label }}</text>
      </view>
    </slot>
    <!-- #ifndef APP-NVUE -->
    <view class="uni-forms-item__content">
      <slot></slot>
      <view class="uni-forms-item__error" :class="{ 'msg--active': msg }">
        <text>{{ msg }}</text>
      </view>
    </view>
    <!-- #endif -->
    <!-- #ifdef APP-NVUE -->
    <view class="uni-forms-item__nuve-content">
      <view class="uni-forms-item__content">
        <slot></slot>
      </view>
      <view class="uni-forms-item__error" :class="{ 'msg--active': msg }">
        <text class="error-text">{{ msg }}</text>
      </view>
    </view>
    <!-- #endif -->
  </view>
</template>

<script>
  /**
   * uni-fomrs-item 表单子组件
   * @description uni-fomrs-item 表单子组件，提供了基础布局已经校验能力
   * @tutorial https://ext.dcloud.net.cn/plugin?id=2773
   * @property {Boolean} required 是否必填，左边显示红色"*"号
   * @property {String } 	label 				输入框左边的文字提示
   * @property {Number } 	labelWidth 			label的宽度，单位px（默认70）
   * @property {String } 	labelAlign = [left|center|right] label的文字对齐方式（默认left）
   * 	@value left		label 左侧显示
   * 	@value center	label 居中
   * 	@value right	label 右侧对齐
   * @property {String } 	errorMessage 		显示的错误提示内容，如果为空字符串或者false，则不显示错误信息
   * @property {String } 	name 				表单域的属性名，在使用校验规则时必填
   * @property {String } 	leftIcon 			【1.4.0废弃】label左边的图标，限 uni-ui 的图标名称
   * @property {String } 	iconColor 		【1.4.0废弃】左边通过icon配置的图标的颜色（默认#606266）
   * @property {String} validateTrigger = [bind|submit|blur]	【1.4.0废弃】校验触发器方式 默认 submit
   * 	@value bind 	发生变化时触发
   * 	@value submit 提交时触发
   * 	@value blur 	失去焦点触发
   * @property {String } 	labelPosition = [top|left] 【1.4.0废弃】label的文字的位置（默认left）
   * 	@value top	顶部显示 label
   * 	@value left	左侧显示 label
   */

  export default {
    name: 'uniFormsItem',
    options: {
      // #ifdef MP-TOUTIAO
      virtualHost: false,
      // #endif
      // #ifndef MP-TOUTIAO
      virtualHost: true
      // #endif
    },
    provide() {
      return {
        uniFormItem: this
      }
    },
    inject: {
      form: {
        from: 'uniForm',
        default: null
      }
    },
    props: {
      // 表单校验规则
      rules: {
        type: Array,
        default() {
          return null
        }
      },
      // 表单域的属性名，在使用校验规则时必填
      name: {
        type: [String, Array],
        default: ''
      },
      required: {
        type: Boolean,
        default: false
      },
      label: {
        type: String,
        default: ''
      },
      // label的宽度
      labelWidth: {
        type: [String, Number],
        default: ''
      },
      // label 居中方式，默认 left 取值 left/center/right
      labelAlign: {
        type: String,
        default: ''
      },
      // 强制显示错误信息
      errorMessage: {
        type: [String, Boolean],
        default: ''
      },
      // 1.4.0 弃用，统一使用 form 的校验时机
      // validateTrigger: {
      // 	type: String,
      // 	default: ''
      // },
      // 1.4.0 弃用，统一使用 form 的label 位置
      // labelPosition: {
      // 	type: String,
      // 	default: ''
      // },
      // 1.4.0 以下属性已经废弃，请使用  #label 插槽代替
      leftIcon: String,
      iconColor: {
        type: String,
        default: '#606266'
      }
    },
    data() {
      return {
        errMsg: '',
        userRules: null,
        localLabelAlign: 'left',
        localLabelWidth: '70px',
        localLabelPos: 'left',
        border: false,
        isFirstBorder: false
      }
    },
    computed: {
      // 处理错误信息
      msg() {
        return this.errorMessage || this.errMsg
      }
    },
    watch: {
      // 规则发生变化通知子组件更新
      'form.formRules'(val) {
        // TODO 处理头条vue3 watch不生效的问题
        // #ifndef MP-TOUTIAO
        this.init()
        // #endif
      },
      'form.labelWidth'(val) {
        // 宽度
        this.localLabelWidth = this._labelWidthUnit(val)
      },
      'form.labelPosition'(val) {
        // 标签位置
        this.localLabelPos = this._labelPosition()
      },
      'form.labelAlign'(val) {}
    },
    created() {
      this.init(true)
      if (this.name && this.form) {
        // TODO 处理头条vue3 watch不生效的问题
        // #ifdef MP-TOUTIAO
        this.$watch('form.formRules', () => {
          this.init()
        })
        // #endif

        // 监听变化
        this.$watch(
          () => {
            const val = this.form._getDataValue(this.name, this.form.localData)
            return val
          },
          (value, oldVal) => {
            const isEqual = this.form._isEqual(value, oldVal)
            // 简单判断前后值的变化，只有发生变化才会发生校验
            // TODO  如果 oldVal = undefined ，那么大概率是源数据里没有值导致 ，这个情况不哦校验 ,可能不严谨 ，需要在做观察
            // fix by mehaotian 暂时取消 && oldVal !== undefined ，如果formData 中不存在，可能会不校验
            if (!isEqual) {
              const val = this.itemSetValue(value)
              this.onFieldChange(val, false)
            }
          },
          {
            immediate: false
          }
        )
      }
    },
    // #ifndef VUE3
    destroyed() {
      if (this.__isUnmounted) return
      this.unInit()
    },
    // #endif
    // #ifdef VUE3
    unmounted() {
      this.__isUnmounted = true
      this.unInit()
    },
    // #endif
    methods: {
      /**
       * 外部调用方法
       * 设置规则 ，主要用于小程序自定义检验规则
       * @param {Array} rules 规则源数据
       */
      setRules(rules = null) {
        this.userRules = rules
        this.init(false)
      },
      // 兼容老版本表单组件
      setValue() {
        // console.log('setValue 方法已经弃用，请使用最新版本的 uni-forms 表单组件以及其他关联组件。');
      },
      /**
       * 外部调用方法
       * 校验数据
       * @param {any} value 需要校验的数据
       * @param {boolean} 是否立即校验
       * @return {Array|null} 校验内容
       */
      async onFieldChange(value, formtrigger = true) {
        const { formData, localData, errShowType, validateCheck, validateTrigger, _isRequiredField, _realName } = this.form
        const name = _realName(this.name)
        if (!value) {
          value = this.form.formData[name]
        }
        // fixd by mehaotian 不在校验前清空信息，解决闪屏的问题
        // this.errMsg = '';

        // fix by mehaotian 解决没有检验规则的情况下，抛出错误的问题
        const ruleLen = this.itemRules.rules && this.itemRules.rules.length
        if (!this.validator || !ruleLen || ruleLen === 0) return

        // 检验时机
        // let trigger = this.isTrigger(this.itemRules.validateTrigger, this.validateTrigger, validateTrigger);
        const isRequiredField = _isRequiredField(this.itemRules.rules || [])
        let result = null
        // 只有等于 bind 时 ，才能开启时实校验
        if (validateTrigger === 'bind' || formtrigger) {
          // 校验当前表单项
          result = await this.validator.validateUpdate(
            {
              [name]: value
            },
            formData
          )

          // 判断是否必填,非必填，不填不校验，填写才校验 ,暂时只处理 undefined  和空的情况
          if (!isRequiredField && (value === undefined || value === '')) {
            result = null
          }

          // 判断错误信息显示类型
          if (result && result.errorMessage) {
            if (errShowType === 'undertext') {
              // 获取错误信息
              this.errMsg = !result ? '' : result.errorMessage
            }
            if (errShowType === 'toast') {
              uni.showToast({
                title: result.errorMessage || '校验错误',
                icon: 'none'
              })
            }
            if (errShowType === 'modal') {
              uni.showModal({
                title: '提示',
                content: result.errorMessage || '校验错误'
              })
            }
          } else {
            this.errMsg = ''
          }
          // 通知 form 组件更新事件
          validateCheck(result ? result : null)
        } else {
          this.errMsg = ''
        }
        return result ? result : null
      },
      /**
       * 初始组件数据
       */
      init(type = false) {
        const { validator, formRules, childrens, formData, localData, _realName, labelWidth, _getDataValue, _setDataValue } = this.form || {}
        // 对齐方式
        this.localLabelAlign = this._justifyContent()
        // 宽度
        this.localLabelWidth = this._labelWidthUnit(labelWidth)
        // 标签位置
        this.localLabelPos = this._labelPosition()
        // 将需要校验的子组件加入form 队列
        this.form && type && childrens.push(this)

        if (!validator || !formRules) return
        // 判断第一个 item
        if (!this.form.isFirstBorder) {
          this.form.isFirstBorder = true
          this.isFirstBorder = true
        }

        // 判断 group 里的第一个 item
        if (this.group) {
          if (!this.group.isFirstBorder) {
            this.group.isFirstBorder = true
            this.isFirstBorder = true
          }
        }
        this.border = this.form.border
        // 获取子域的真实名称
        const name = _realName(this.name)
        const itemRule = this.userRules || this.rules
        if (typeof formRules === 'object' && itemRule) {
          // 子规则替换父规则
          formRules[name] = {
            rules: itemRule
          }
          validator.updateSchema(formRules)
        }
        // 注册校验规则
        const itemRules = formRules[name] || {}
        this.itemRules = itemRules
        // 注册校验函数
        this.validator = validator
        // 默认值赋予
        this.itemSetValue(_getDataValue(this.name, localData))
      },
      unInit() {
        if (this.form) {
          const { childrens, formData, _realName } = this.form
          childrens.forEach((item, index) => {
            if (item === this) {
              this.form.childrens.splice(index, 1)
              delete formData[_realName(item.name)]
            }
          })
        }
      },
      // 设置item 的值
      itemSetValue(value) {
        const name = this.form._realName(this.name)
        const rules = this.itemRules.rules || []
        const val = this.form._getValue(name, value, rules)
        this.form._setDataValue(name, this.form.formData, val)
        return val
      },

      /**
       * 移除该表单项的校验结果
       */
      clearValidate() {
        this.errMsg = ''
      },

      // 是否显示星号
      _isRequired() {
        // TODO 不根据规则显示 星号，考虑后续兼容
        // if (this.form) {
        // 	if (this.form._isRequiredField(this.itemRules.rules || []) && this.required) {
        // 		return true
        // 	}
        // 	return false
        // }
        return this.required
      },

      // 处理对齐方式
      _justifyContent() {
        if (this.form) {
          const { labelAlign } = this.form
          let labelAli = this.labelAlign ? this.labelAlign : labelAlign
          if (labelAli === 'left') return 'flex-start'
          if (labelAli === 'center') return 'center'
          if (labelAli === 'right') return 'flex-end'
        }
        return 'flex-start'
      },
      // 处理 label宽度单位 ,继承父元素的值
      _labelWidthUnit(labelWidth) {
        // if (this.form) {
        // 	const {
        // 		labelWidth
        // 	} = this.form
        return this.num2px(this.labelWidth ? this.labelWidth : labelWidth || (this.label ? 70 : 'auto'))
        // }
        // return '70px'
      },
      // 处理 label 位置
      _labelPosition() {
        if (this.form) return this.form.labelPosition || 'left'
        return 'left'
      },

      /**
       * 触发时机
       * @param {Object} rule 当前规则内时机
       * @param {Object} itemRlue 当前组件时机
       * @param {Object} parentRule 父组件时机
       */
      isTrigger(rule, itemRlue, parentRule) {
        //  bind  submit
        if (rule === 'submit' || !rule) {
          if (rule === undefined) {
            if (itemRlue !== 'bind') {
              if (!itemRlue) {
                return parentRule === '' ? 'bind' : 'submit'
              }
              return 'submit'
            }
            return 'bind'
          }
          return 'submit'
        }
        return 'bind'
      },
      num2px(num) {
        if (typeof num === 'number') {
          return `${num}px`
        }
        return num
      }
    }
  }
</script>

<style lang="scss">
  .uni-forms-item {
    position: relative;
    display: flex;
    /* #ifdef APP-NVUE */
    // 在 nvue 中，使用 margin-bottom error 信息会被隐藏
    padding-bottom: 22px;
    /* #endif */
    /* #ifndef APP-NVUE */
    margin-bottom: 22px;
    /* #endif */
    flex-direction: row;

    &__label {
      display: flex;
      flex-direction: row;
      align-items: center;
      text-align: left;
      font-size: 14px;
      color: #606266;
      height: 36px;
      padding: 0 12px 0 0;
      /* #ifndef APP-NVUE */
      vertical-align: middle;
      flex-shrink: 0;
      /* #endif */

      /* #ifndef APP-NVUE */
      box-sizing: border-box;

      /* #endif */
      &.no-label {
        padding: 0;
      }
    }

    &__content {
      /* #ifndef MP-TOUTIAO */
      // display: flex;
      // align-items: center;
      /* #endif */
      position: relative;
      font-size: 14px;
      flex: 1;
      /* #ifndef APP-NVUE */
      box-sizing: border-box;
      /* #endif */
      flex-direction: row;

      /* #ifndef APP || H5 || MP-WEIXIN || APP-NVUE */
      // TODO 因为小程序平台会多一层标签节点 ，所以需要在多余节点继承当前样式
      & > uni-easyinput,
      & > uni-data-picker {
        width: 100%;
      }

      /* #endif */
    }

    & .uni-forms-item__nuve-content {
      display: flex;
      flex-direction: column;
      flex: 1;
    }

    &__error {
      color: #f56c6c;
      font-size: 12px;
      line-height: 1;
      padding-top: 4px;
      position: absolute;
      /* #ifndef APP-NVUE */
      top: 100%;
      left: 0;
      transition: transform 0.3s;
      transform: translateY(-100%);
      /* #endif */
      /* #ifdef APP-NVUE */
      bottom: 5px;
      /* #endif */

      opacity: 0;

      .error-text {
        // 只有 nvue 下这个样式才生效
        color: #f56c6c;
        font-size: 12px;
      }

      &.msg--active {
        opacity: 1;
        transform: translateY(0%);
      }
    }

    // 位置修饰样式
    &.is-direction-left {
      flex-direction: row;
    }

    &.is-direction-top {
      flex-direction: column;

      .uni-forms-item__label {
        padding: 0 0 8px;
        line-height: 1.5715;
        text-align: left;
        /* #ifndef APP-NVUE */
        white-space: initial;
        /* #endif */
      }
    }

    .is-required {
      // color: $uni-color-error;
      color: #dd524d;
    }
  }

  .uni-forms-item--border {
    margin-bottom: 0;
    padding: 10px 0;
    // padding-bottom: 0;
    border-top: 1px #eee solid;

    /* #ifndef APP-NVUE */
    .uni-forms-item__content {
      flex-direction: column;
      justify-content: flex-start;
      align-items: flex-start;

      .uni-forms-item__error {
        position: relative;
        top: 5px;
        left: 0;
        padding-top: 0;
      }
    }

    /* #endif */

    /* #ifdef APP-NVUE */
    display: flex;
    flex-direction: column;

    .uni-forms-item__error {
      position: relative;
      top: 0px;
      left: 0;
      padding-top: 0;
      margin-top: 5px;
    }

    /* #endif */
  }

  .is-first-border {
    /* #ifndef APP-NVUE */
    border: none;
    /* #endif */
    /* #ifdef APP-NVUE */
    border-width: 0;
    /* #endif */
  }
</style>
